#include "./work_service.h"
#include "../driver/parts.h"
#include "../driver/sensor.h"
#include "../utils/progress.h"
#include "../utils/alg.h"
#include "../driver/nozzle_motor.h"
#include "../task/heat_task.h"
#include "../driver/mux.h"
#include "../driver/door_motor.h"
#include "./config.h"

namespace service {

constexpr uint32_t DRAW_TIMEOUT = 60 * 1000;

using namespace task;
using namespace utils;
using namespace driver;

static void water_heat_open() {
    heatTask->stop();
    osDelay(100);
    parts->water_heat_open();
}

static void water_heat_close() {
    parts->water_heat_close();
    heatTask->reset();
}

// 300ml / 60s
// 5ml / s / 1000ms
// 1ml => 200ms
Error draw_water(uint8_t ml) {
    sensor->flow_reset();
    parts->water_pump_open();
    osDelay(config->pump_ml_ms * ml);
    parts->water_pump_close();
    return ec::OK;
}

Error draw_water_with_flow(uint8_t ml) {
    Error err;
    Progress prog;
    uint32_t time_count = 0;
    uint16_t flow_value = 0;
    sensor->flow_reset();
    parts->water_pump_open();

    for (;;) {
        uint16_t cur = sensor->flow_volume();
        if (cur >= ml) {
            break;
        }

        if (prog.is_after(5000)) {
            if (alg::abs(cur, flow_value) < 5) {
                err = ec::FLOW_FAIL;
                goto finish;
            }  
            flow_value = cur;
            prog.reset(); 
        }

        osDelay(50);
    
        time_count += 50;
        if (time_count >= DRAW_TIMEOUT) {
            err = ec::DRAW_TIMEOUT;
            goto finish;
        }
    }
finish:
    parts->water_pump_close();
    return err;
}

Error clean(const CleanParam &param) {
    Progress prog;
    Error err = nozzleMotor->run(param.c);
    if (err) {
        goto finish;
    }

    water_heat_open();
    err = draw_water(param.drain_cold_water);
    water_heat_close();
    if (err) {
        goto finish;
    }

    prog.reset();

    parts->valve_open(3);
    parts->valve_open(4);
    
    for (; !prog.is_after(param.steam_duration * 1000);) {

        err = nozzleMotor->run(param.c);
        if (err) {
            goto finish;
        }
        err = nozzleMotor->run(param.d);
        if (err) {
            goto finish;
        }
    }

    parts->valve_close(3);
    parts->valve_close(4);

    err = nozzleMotor->run(0);
    if (err) {
        goto finish;
    }

finish:
    parts->valve_close(3);
    parts->valve_close(4);

    return err;
}

Error cooking(const CookingParam &cooking) {
    Progress prog;
    Error err;
    if (!mux->is_bowl_near()) {
        err = ec::NOT_BOWL;
        goto finish;
    }
    water_heat_open();

    err = nozzleMotor->run(0);
    if (err) {
        goto finish;
    }
    err = doorMotor->close();
    if (err) {
        goto finish;
    }

    err = nozzleMotor->run(cooking.k);
    if (err) {
        goto finish;
    }    

    err = draw_water(cooking.pre_water);
    water_heat_close();
    if (err) {
        goto finish;
    }

    err = nozzleMotor->run(cooking.f);
    if (err) {
        goto finish;
    }

    prog.reset();
    parts->valve_open(3);
    for (; !prog.is_after(cooking.defroze_duration * 1000);) {
        err = nozzleMotor->run(cooking.f);
        if (err) {
            goto finish;
        }
        err = nozzleMotor->run(cooking.g);
        if (err) {
            goto finish;
        }
    }
    parts->valve_close(3);

    water_heat_open();
    err = nozzleMotor->run(cooking.e);
    if (err) {
        goto finish;
    }

    err = draw_water(cooking.pour_water);
    water_heat_close();
    if (err) {
        goto finish;
    }

    err = nozzleMotor->run(cooking.h);
    if (err) {
        goto finish;
    }

    parts->valve_open(3);
    osDelay(cooking.mix_soup_duration * 1000);
    parts->valve_close(3);

    err = nozzleMotor->run(cooking.i);
    if (err) {
        goto finish;
    }

    parts->valve_open(3);
    osDelay(cooking.clogging_prevent_duration * 1000);
    parts->valve_close(3);

    err = nozzleMotor->run(0);
    if (err) {
        goto finish;
    }
    err = doorMotor->open();
    if (err) {
        goto finish;
    }
finish:
    parts->valve_close(3);
    water_heat_close();
    return err;
}


}
